package my.suveng.util.redis;

import cn.hutool.core.lang.caller.CallerUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import my.suveng.util.json.Jackson;
import my.suveng.util.log.PlusLogFactoryHutool;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.ObjectUtils;

import javax.persistence.Id;
import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;

/**
 * redis dao 封装
 * T 是 mysql对应的实体类
 * K 是 实体类对应的主键
 * @author suwenguang
 **/
public class RedisDao<T, K> {
    //hutool日志
    private static final Log log = LogFactory.setCurrentLogFactory(new PlusLogFactoryHutool()).getLog(CallerUtil.getCaller().getName());

    //redis的客户端
    private final StringRedisTemplate redisCleint;

    public RedisDao(StringRedisTemplate redisCleint) {
        this.redisCleint = redisCleint;
    }

    /**
     * 序列化mysql对象到redis中
     * 使用json字符串形式
     * 对象必须包含{@link Id} 的字段
     *
     * @author suwenguang
     */
    public boolean saveById(T mysql, long time) {
        // redis的key
        String key = getKey(mysql);

        if (key == null) {
            throw new IllegalArgumentException("key生成失败,检查实体类的Id注解字段");
        }

        // 存入redis,默认3个小时
        save(key, Jackson.toJsonString(mysql), time > 0 ? time : 3 * 60);

        return StrUtil.isNotBlank(redisCleint.opsForValue().get(key));
    }

    /**
     * 删除对象
     * @author suwenguang
     */
    public Boolean delete(Class<T> clazz, K id) {
        String key = getKey(clazz, id);
        return redisCleint.delete(key);
    }


    /**
     * 根据id查找对象
     * @author suwenguang
     */
    public T findByKey(Class<T> clazz, K id) {
        String key = getKey(clazz, id);
        String json = redisCleint.opsForValue().get(key);
        return Jackson.parseJson(json, clazz);
    }


    /**
     * 保存到redis
     * @author suwenguang
     * @param key redis的key
     * @param value redis的value
     * @param time redis的过期时间, 分钟为单位
     */
    private void save(String key, String value, long time) {
        redisCleint.opsForValue().set(key, value, time, TimeUnit.MINUTES);
    }

    /**
     * 获取对象的key
     * @param mysql 对象
     * @return key
     */
    private String getKey(T mysql) {
        // 构建key, 使用对象的name值作为前缀
        String keyPrefix = getKeyPrefix(mysql.getClass());
        //获取主键字段的tostring的值
        Field idField = getIdField(mysql.getClass());

        try {
            Object id = idField.get(mysql);
            return keyPrefix + ":" + id.toString();
        } catch (IllegalAccessException e) {
            log.error(e);
            return null;
        }
    }


    /**
     * 获取key
     * @author suwenguang
     */
    private String getKey(Class<T> clazz, K id) {
        // 构建key, 使用对象的name值作为前缀
        String keyPrefix = getKeyPrefix(clazz);
        return keyPrefix + ":" + id.toString();
    }

    private String getKeyPrefix(Class<?> clazz) {
        return clazz.getSimpleName();
    }

    private Field getIdField(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        Field idField = null;
        for (Field field : fields) {
            Id annotation = field.getAnnotation(Id.class);
            if (annotation != null) {
                idField = field;
                break;
            }
        }

        if (ObjectUtils.isEmpty(idField)) {
            throw new IllegalArgumentException("没有含有Id注解的字段");
        }
        idField.setAccessible(true);

        return idField;
    }
}
